This page last changed on May 02, 2007 by scytacki.

Using Ruby on Rails at CC

Start here: Ruby on Rails, the RoR API online.

If you are using a Mac this article is useful: Building Ruby, Rails, Subversion, Mongrel, and MySQL on Mac OS X - there are some good reasons to rebuild Ruby from source. If the page won't load here's the Google cache:

put the following into your ~/.irbrc file to enable tabbed completion:

require 'irb/completion'
ARGV.concat [TSC: "--readline", "--prompt-mode", "simple" ]

Err the blog has a great article on more to add to irb for rails debugging: http://errtheblog.com/post/43

Some great rails blogs by some of the core rails developers:

The best rails blog software: Mephisto.

I keep a copy of Rails (released and edge), Mephisto, all of technoweenies plugins and why's code checked out of their subversion repositories and use them as reference to see how well designed rails and ruby code works.

On Ruby

There's a guy named "why the lucky stiff", his blog and code pages are also quite interesting:

One of his projects is Hpricot: http://code.whytheluckystiff.net/hpricot/wiki
He also wrote a introductory book: http://code.whytheluckystiff.net/poignant/
This documentation assumes you are Using Subversion at CC.

Alternate Ruby web frameworks

  • Merb
    • Very nice lightweight Ruby web stack created by Ezra Zygmuntowicz that is designed for efficient threading and to work well with Mongrel.
  • Camping
    • A tiny elegant subset of Rails

Recommended Books

I recommend getting these books. I bought the combined paper and pdf – both forms are quite useful in different ways.

  1. Programming Ruby, The Pragmatic Programmer's Guide, Second Edition
  2. Agile Web Development with Rails, Second Edition
  3. Rails Recipes
  4. Rails for Java Developers

Misc Useful Tools

Rails Plugins

Take a look at these two plugins for Rails. They are very uselful and very easy to use for a web application programmer. If you look under the hood they are also very interesting examples of Ruby code and the ease with which meta-programming can be done in Ruby and in Rails.

  1. acts_as_authenticated
  2. attachment_fu

JRuby

JRuby is an implementation of the Ruby VM in Java. Version 0.9.3 will shortly be released.

Projects at Concord

Here's a list of the Rails microportals I've developed so far at CC:

This is a simple Rails app. It allows you to create simple learning activities that get built into custom xml (otml) and passed to a the TEEMSS2 Java application SensorPortfolio through a custom jnlp builder.

The development version of the TEEMSS2 DIY site which uses the SDS as for learner data persistence.

The Seeing Math site is a simple variation of the TEEMSS2 site that delivers applets instead of Java webstart applications.

Deprecated

In July I wrote a variant that supports a SAIL-based TELS-PAS learner environment using a REST web services architecture. A simple REST-based services model is used to send data between the portal and the Java PAS Learner Runtime system.

In addition to the REST based learner data turnaround the app is doing a great deal of xml hacking to create reports on learner interaction with rich models (I'll bet there are much better ways to accomplish what I want). See the Model Schemas link in this page:

The Sail Data Service (SDS)

Most of these apps use The Concord Consortium's General Members database (CCMembers) for user authentication.


Setting up Rails on an Existing Server

older content below ... needs editing and updating

At Concord we have two main "web" servers, one for general pages and php/database applications, and another for project web applications. That was the server onto which we put the Rails setup.

If you are using a package management system, like RPMs, it's pretty easy to get started. 

Install the most recent versions of Ruby, Rails, and Lighthttpd.

Set up your apache to proxy through requests to Lighthttpd.  Something like this:

<VirtualHost \*:80 \*:8080>
ServerAdmin webmaster@concord.org
ServerName rails.dev.concord.org
ProxyPass / [http://127.0.0.1:8580/]
ProxyPassReverse / [http://127.0.0.1:8580/]
ProxyTimeout 1200
ErrorLog /var/log/httpd/rails.dev.concord.org-error_log
CustomLog /var/log/httpd/rails.dev.concord.org-access_log combined
</VirtualHost>

In the ProxyPass directive, you list whatever port your Lighthttpd server is running on. 

To enable HTTPS/SSL support, get an SSL certificate and add an additional virtual host to the apache configuration, like the following:

<VirtualHost \*:443>
ServerAdmin webmaster@concord.org
ServerName rails.dev.concord.org
SSLEngine on
SSLCertificateFile /usr/share/ssl/certs/mycert.crt
SSLCertificateKeyFile /usr/share/ssl/certs/mycert.key
SSLCertificateChainFile /usr/share/ssl/certs/sf_issuing.crt
ProxyPass / [http://127.0.0.1:8580/]
ProxyPassReverse / [http://127.0.0.1:8580/]
ProxyTimeout 1200
ErrorLog /var/log/httpd/rails-ssl.dev.concord.org-error_log
CustomLog /var/log/httpd/rails-ssl.dev.concord.org-access_log combined
</VirtualHost>

Creating a Rails application from scratch and getting it onto the development server.

I'm assuming you have ruby, gems, and mysql working on your computer.

Updated Steps

Here's what I do now to start an application calls rest:

svn mkdir -m "creating app root dir in repository" svn+ssh://svn.rails.dev.concord.org/home/subversion/projects/trunk/common/rails/rest
mysqladmin -u root -p****** create rest_development
mysqladmin -u root -p****** create rest_test
mysqladmin -u root -p****** create rest_production
svn co svn+ssh://svn.rails.dev.concord.org/home/subversion/projects/trunk/common/rails/rest
rails rest
cd rest
rmdir components
mv config/database.yml config/database_sample.yml
svn add config app db doc lib public script test vendor
svn add --non-recursive log tmp
svn propset svn:ignore '*' log tmp
cp config/database_sample.yml config/database.yml
svn propset svn:ignore 'database.yml' config
svn add Rakefile README
svn commit -m "initial check in"

Then I use Piston to install Edge Rails and a series of plugins that will be needed:

piston import http://dev.rubyonrails.org/svn/rails/trunk vendor/rails
piston import http://dev.rubyonrails.com/svn/rails/plugins/ssl_requirement/ vendor/plugins/ssl_requirement 
piston import http://dev.rubyonrails.com/svn/rails/plugins/tzinfo_timezone/ vendor/plugins/tzinfo_timezone
piston import http://dev.rubyonrails.com/svn/rails/plugins/tztime/ vendor/plugins/tztime
piston import http://dev.rubyonrails.com/svn/rails/plugins/simply_helpful/ vendor/plugins/simply_helpful/
piston import http://svn.pragprog.com/Public/plugins/annotate_models vendor/plugins/annotate_models
piston import http://svn.techno-weenie.net/projects/plugins/restful_authentication/ vendor/plugins/restful_authentication
piston import http://svn.umesd.k12.or.us/plugins/query_analyzer  vendor/plugins/query_analyzer
svn commit -m "used piston to check in edge rails and plugins"

Older Instructions

In the directory you do web development open a shell and create the rails application:

rails myapp
cd myapp

This creates a directory with the skeleton of a rails application. I use iTermbecause it supports multiple tabbed windows. I'll assume you are too. Open four tabbed shell windows and cd into myapp in all of them.

Create the mysql databases: myapp_development and myapp_test.

I also use Textmate($75) as a programming editor when I am doing Rails development. If you are using Textmate open the whole myapp folder by doing this in the shell:

mate .

Edit config/database.yml and enter the appropriate values for the database, username, and password:

development:
  adapter: mysql
  host: localhost
  database: myapp_development
  username: root
  password:

test:
  adapter: mysql
  database: myapp_test
  username: root
  password:

Make a copy of this file in config/ without the passwords and label it databse_sample.yml. Don't worry about the production database for now.

Normally you'd create a real application now but for this documentation I'm going to use scaffolds (they'll auto-generate the app) to hack together something very quickly. Select the first tab in iTerm and generate a simple model and database migration for something we'll call a page.

script/generate model page
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/page.rb
      create  test/unit/page_test.rb
      create  test/fixtures/pages.yml
      create  db/migrate
      create  db/migrate/001_create_pages.rb

Anytime you are using a generator you can suffix the command with --pretend to see what the generator will do without executing it.

Edit the db/migrate/001_create_pages.rb as follows:

class CreatePages < ActiveRecord::Migration
  def self.up
    create_table :myapp_pages do |t|
      t.column :name, :string
      t.column :content, :text
    end
  end

  def self.down
    drop_table :myapp_pages
  end
end

When you deploy this application to the development server your tables will be mixed in with all the other tables used by the various rails applications in the database rails on databse.concord.org. In order to keep the table names separate I prefix them with the application name in the migration file and the model. This isn't necessary on your local machine but it makes things simpler. Add this line to app/models/page.rb:

class Page < ActiveRecord::Base
  set_table_name "myapp_pages"
end

One more detail is to set the relative url root (for use only on the deployment server) and to override the default schema_info_table_name with the application name prefix. Add this to config/environment.rb:

# ActionController::AbstractRequest.relative_url_root = "/myapp"

module ActiveRecord
  class Migrator
    def Migrator.schema_info_table_name
      Base.table_name_prefix + "myapp_schema_info" + Base.table_name_suffix
    end
  end
end

Now in the first shell tab run the rake task db:migrate to apply this migration to the myapp_development database and create the table pages. Rake is a Ruby version of Bash's make and Java's ant.

rake db:migrate
(in /Users/stephen/dev/rails/myapp)
== CreatePages: migrating =====================================================
-- create_table(:myapp_pages)
   -> 0.0030s
== CreatePages: migrated (0.0032s) ============================================

That created the table in the mysql database. You'll end up creating a sequence of migration files to create new tables or change existing tables. All of these table are under version control. When you checkout the code to a new server you can recreate the entire database schema by just running the rake migration task.

You can also run migrations in reverse like this:

rake db:migrate VERSION=0
(in /Users/stephen/dev/rails/myapp)
== CreatePages: reverting =====================================================
-- drop_table(:myapp_pages)
   -> 0.2586s
== CreatePages: reverted (0.2588s) ============================================

Now use a scaffold to create a default controller and views (you can get the built-in help by typing script/generate scaffold):

script/generate scaffold page page
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/page
      exists  test/functional/
  dependency  model
      exists    app/models/
      exists    test/unit/
      exists    test/fixtures/
      create    app/models/page.rb
      create    test/unit/page_test.rb
      create    test/fixtures/pages.yml
      create  app/views/page/_form.rhtml
      create  app/views/page/list.rhtml
      create  app/views/page/show.rhtml
      create  app/views/page/new.rhtml
      create  app/views/page/edit.rhtml
      create  app/controllers/page_controller.rb
      create  test/functional/page_controller_test.rb
      create  app/helpers/page_helper.rb
      create  app/views/layouts/page.rhtml
      create  public/stylesheets/scaffold.css

The application is ready to start. Open the second shell tab and start the web server.

script/server webrick
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options
[2006-08-07 23:26:22] INFO  WEBrick 1.3.1
[2006-08-07 23:26:22] INFO  ruby 1.8.4 (2005-12-24) [TSC:i686-darwin8.6.1]
[2006-08-07 23:26:22] INFO  WEBrick::HTTPServer#start: pid=1919 port=3000

You can check this window later to see what requests have been served.

Open a browser and browse to: http://localhost:3000/. You'll see a static rails page titled: "Ruby on Rails: Welcome aboard". This page is nothing special and normally you'd delete it before going much further but we haven't created a route for "/" yet so don't do anything. There is some useful info here, click About your application's environment.

You created a controller called page so try *http://localhost:3000/page*. You should see this:

Listing pages

Name Content

New page

Click on New Page. enter some data for the fields: Name and Content and click the Create button. You should now have a listing of one page item.

Select the third shell tab and open the an IRB session for the application:

script/console
Loading development environment.
>>

This is running an IRB (interactive ruby) session with all of your rails classes and models. Try this:

Page.create(:name => "Another page", :content => "This is more stuff to write")
=> #<Page:0x27598f0 @new_record=false, 
@errors=#<ActiveRecord::Errors:0x2758338 @errors={}, 
@base=#<Page:0x27598f0 ...>>, 
@attributes={"name"=>"Another page", "id"=>2, "content"=>"This is more stuff to write"}>
>> Page.find(:all).each { |p| puts p.name }
test page
Another page
=> [#<Page:0x2751a60 
@attributes={"name"=>"test page", "id"=>"1", "content"=>"And this is the content"}>, 
#<Page:0x2751a24 
@attributes={"name"=>"Another page", "id"=>"2", "content"=>"This is more stuff to write"}>]

I created another Page object and saved it to the database. Then I iterated through all the Page objects passing each in turn to the code block delimited with '{ }' and printed the name attribute. Go back to the browser and click refresh. You should see two page objects. If you make changes in the source use reload! to load the new classes the console.

In the fourth shell tab start a breakpoint service:

script/breakpointer
No connection to breakpoint service at druby://localhost:42531 (DRb::DRbConnError)
Tries to connect will be made every 2 seconds...

Now open app/controllerspage_controller.rb and enter a breakpoint at the start of the show method:

def show
    breakpoint
    @page = Page.find(params[TSC::id])
  end

Now in the browser click the Show link for the first page. The browser should pause while loading the page, Take a look in the breakpointer shell:

Executing break point at ./script/../config/../app/controllers/page_controller.rb:16 in `show'
irb(#<PageController:0x26d5460>):001:0>

You can inspect and change variables, execute object methods, even redefine classes. In the example below I am inspecting the params hash, looking at the page object it is going to be used to reference and then chaging the id value of the hash and exiting the breakpoint irb session.

Executing break point at ./script/../config/../app/controllers/page_controller.rb:16 in `show'
irb(#<PageController:0x26d5460>):001:0> params
=> {"action"=>"show", "id"=>"1", "controller"=>"page"}
irb(#<PageController:0x26d5460>):002:0> @page = Page.find(params[TSC::id])
=> #<Page:0x2762cc0 @attributes={"name"=>"test page", "id"=>"1", "content"=>"And this is the content"}>
irb(#<PageController:0x26d5460>):003:0> params[TSC::id]
=> "1"
irb(#<PageController:0x26d5460>):004:0> params[TSC::id]=2
=> 2
irb(#<PageController:0x26d5460>):005:0> exit

Server exited. Closing connection...

No connection to breakpoint service at druby://localhost:42531 (DRb::DRbConnError)
Tries to connect will be made every 2 seconds...

The browser will now display the second page object. Notice how the params hash related to the url. This is the url the browser tried to load: http://localhost:3000/page/show/1 - page is the controller, show is the method, and 1is the :id. In the breakpoint session we changed the value of :id to 2 and this is the page object the browser rendered instead.


Now use the instructions here Using Subversion at CCto import your application to a subversion repository. There are a number of steps you need to take before doing the initial import. There are a number of files in the rails application directory which you don't want to copy.

This blog article has a good overview: http://blog.teksol.info/articles/2006/03/09/subversion-primer-for-rails-projects

Don't add these files, instead copy them manually to the development server. The file database.yml has the database passwords so you don't ever want to check this in to any repository which might be publically accesible. These files will also need to be edited before the deployment will work.

config/database.yml
config/environment.rb

If you accidently added these files to subversion before you commit them you can reverse this operation with the revert command:

svn revert config/database.yml
svn revert config/environment.rb

If your Rails app is up and working first make copies of these files:

cp config/database.yml config/database.yml.sample
cp config/environment.rb config/environment.rb.sample

Then remove the passwords from the sample files and add them to subversion. This will give anyone who chacks out a copy of the project a good template to start from.

Don't add these files either - you don't want these managed by subversion. The log files are written to by LightTPD the web server and db/schema.rb is managed by running the migrations on the server.

svn propset svn:ignore "*.log" log
svn propset svn:ignore "schema.db" db
svn propset svn:ignore "*" tmp/sessions tmp/cache tmp/sockets
svn propset svn:ignore "*" tmp
svn propset svn:ignore "database.yml" config
svn propset svn:ignore "environment.rb" config

I had trouble with the last two. They seemed to overwrite each other. Try adding the files in config/ to ignore using your editor with this command:

svn propedit svn:ignore config

If you are on a Mac you should also have svn ignore the '.DS_Store' it scatters in directories for use in caching the finder settings. You can set global ignore settings for svn in you ~/.subversion folder.

svn propset svn:ignore -R ".DS_Store"

When you create a bunch of new files which need to be added to subversion you can use the following shell command to add every file whose subversion status is '?':

svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add

If you use this command frequently add it to your .bash_profile as an alias:

alias svnaddall=svn status | grep '^\?' | sed -e 's/? *//' | sed -e 's/ /\ /g' | xargs svn add

Checkout on the server

Right now the rails development server and the subversion server are on the same host so you can use a file type url to reference the repository location. The advantage is that if you won't be asked for your password when doing checkouts or commits.

cd /web/rails.dev.concord.org/
svn file:///home/subversion/myapp/trunk myapp

This will checkout a copy of the trunk from the myapp subversion repository.

Reset the permissions on myapp/log to allow the web server to create and write it's log files:

cd myapp
chmod 777 log

Recreate the files db/database.yml and config/environment.rb.

Edit the lighttpd.conf file and just copy one of the other rails app blocks and replace the app name with myapp. You will need sudo access to edit this file.

sudo emacs /etc/lighttpd/lighttpd.conf

Now restart lighttpd.

sudo /etc/rc.d/init.d/lighttpd restart

database.sample.yml (application/octet-stream)
Document generated by Confluence on Jan 27, 2014 16:56